/**
 * 
 */
package com.sj.szxy.courseschedule.service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import javax.print.attribute.standard.Copies;

import com.sj.szxy.courseschedule.constrant.CompoundConstraint;
import com.sj.szxy.courseschedule.constrant.ExcludeTeacherConstraint;
import com.sj.szxy.courseschedule.entity.AdministrativeClass;
import com.sj.szxy.courseschedule.entity.ClassSegment;
import com.sj.szxy.courseschedule.entity.Noon;
import com.sj.szxy.courseschedule.entity.Segment;
import com.sj.szxy.courseschedule.entity.Subject;
import com.sj.szxy.courseschedule.entity.Teacher;
import com.sj.szxy.courseschedule.entity.TeachingClass;
import com.sj.szxy.courseschedule.entity.course.AdministractiveCourse;
import com.sj.szxy.courseschedule.entity.course.Course;

/**
 * @author Yi Ping
 * @date 2018年3月29日 下午3:11:20 
 * @since 1.0.0
 *
 */
public class ScheduleCourseService {
	
	private int[] weekdays = new int[] {1,2,3,4,5};
	private int courseTimesInMorning = 4;
	private int courseTimesInAfternoon = 4;
	private int courseTimesInNight = 0;
	private List<Subject> subjects = new ArrayList<>();					// 所有的科目
	private int index_of_subjects = 0;
	private Set<AdministrativeClass> classes = new HashSet<>();			// 所欲的行政班级
	private Deque<ClassSegment> segments = new LinkedList<>(); 				//所有的班级排课时间
	private List<Teacher> teachers = new ArrayList<>(); 				// 所有的老师
	private Deque<Course> courses = new LinkedList<>();					// 所有的课程
	
	private List<Course> scheduledCourses = new ArrayList<>();
	private Set<Course> unScheuledCourses = new HashSet<>();
	
	/** for validate schedule variable**/
	private Map<Segment,List<Course>> segmentCourseMap = new HashMap<>();
	private Map<Segment,Set<Teacher>> segmentTeacherMap = new HashMap<>();
	private Map<Segment, List<AdministrativeClass>> segmentClassMap  = new HashMap<>();
	
	private Map<Teacher, List<Course>> teacherCourseMap = new HashMap<>();
	private int teacherCourseCount = 0;
	/**
	 * 
	 */
	public ScheduleCourseService() {
		super();
		start();
	}


	public void start() {
		initSubjects();
		initAdministractiveClass();
		initTeachers();
		initCourses();
		initSegment();
		
		
		
	}
	
	
	/**
	 * 
	 */
	private void initSegment() {
		// 初始化segment;
		for(int i = 0; i<weekdays.length; i++ ) {
			Noon noon = Noon.MORNING;
			for(int j=0; j< courseTimesInMorning + courseTimesInAfternoon + courseTimesInNight; j++) {
				if(j<courseTimesInMorning) {
					noon = Noon.MORNING;
				}else if(j< courseTimesInMorning + courseTimesInAfternoon) {
					noon = Noon.AFTERNOON;
				}else {
					noon= Noon.NIGTH;
				}
				
				ClassSegment seg = new ClassSegment(i, j+1, noon );
				seg.addAllAdministractiveClass(classes);
				segments.add(seg);
			}
		}
	}


	/**
	 * 
	 */
	private void initCourses() {
		// TODO 以下为生成模拟数据
		for (AdministrativeClass class1: classes) {
			
			for(int i=0; i< 40; i++) {
				Subject subject = getRandomSubject();
				courses.add(new AdministractiveCourse(subject, getRandomTeacher(subject), class1));
			}
		}
	}


	/**
	 * 
	 */
	private void initTeachers() {
		// TODO
		for(Subject subject : subjects)
			for(int i = 0 ; i<2; i++) {
				teachers.add(new Teacher(subject.getName()+ "老师" + (i+1), subject ));
			}
	}

	public void showTeachers() {
		for(Teacher teacher:teachers) {
			System.out.println(teacher);
		}
	}
	
	private Teacher getRandomTeacher(Subject subject) {
		Random random = new Random();
		int i = random.nextInt(teachers.size()-1);
		Teacher teacher = teachers.get(i);
		while (!teacher.getSubject().equals(subject)) {
			i = random.nextInt(teachers.size()-1);
			teacher = teachers.get(i);
		}
		
		return teacher;
	}
	
	private Subject getRandomSubject() {
		Random random = new Random();
		return subjects.get(random.nextInt(subjects.size()-1));
//		if(index_of_subjects >= subjects.size()) {
//			index_of_subjects =0;
//		}
//		Subject subject = subjects.get(index_of_subjects);
//		index_of_subjects++;
//		return subject;
	}
	
	/**
	 * 
	 */
	private void initAdministractiveClass() {
		// TODO Auto-generated method stub
		// 模拟数据
		
		for(int i = 0; i<10; i++) {
			classes.add(new AdministrativeClass("班级" + chineseNumber[i]));
		}
	}
	
	private String[] chineseNumber = new String[] {"一", "二", "三", "四", "五", "六", "七", "八", "九", "十"};
	

	private  void initSubjects() {
		subjects.add(Subject.CHINESE);
		subjects.add(Subject.ENGLISH);
		subjects.add(Subject.MATH);
		subjects.add(Subject.SPORT);
		subjects.add(Subject.MUSIC);
		subjects.add(Subject.CARRER);
		subjects.add(Subject.PHYSICAL);
		subjects.add(Subject.BIOLOGY);
		subjects.add(Subject.CHEMISTRY);
		subjects.add(Subject.GEOGRAPHY);
		subjects.add(Subject.TECHNOLOGY);
		subjects.add(Subject.HISTORY);
		subjects.add(Subject.POLITICS);
		subjects.add(new Subject("美术",false));
	}
	
	/**
	 * 排课逻辑
	 */
	public void doShedule(Deque<Course> courses, Deque<ClassSegment> segments) {
		this.unScheuledCourses.clear();
		this.scheduledCourses.clear();
		this.teacherCourseMap.clear();
		this.segmentCourseMap.clear();
		resetSegments(segments);
		initSegment();
		
		boolean flag = false;
		for(Course course: courses) {
			flag = false;  //  单个课程成功的标识
			for(int i= 0; i< segments.size(); i++) {
				ClassSegment segment =(ClassSegment) segments.pop();
				if(_doScheduleInternal(segment, course)) {
					flag = true;
				}
				
				segments.add(segment);
				if(flag)
					break;
			}
			if(!flag) {
				unScheuledCourses.add(course);
			}else {
				scheduledCourses.add(course);
			}
			
		}
		
	}
	
	
	/**
	 * @param segments2
	 */
	private void resetSegments(Deque<ClassSegment> segments2) {
		for (ClassSegment segment : segments2) {
			segment.setConstraint(new CompoundConstraint());
			segment.addAllAdministractiveClass(classes);
		}
	}


	/**
	 * 显示排课结果
	 */
	public void showResult() {
		System.out.println("........显示排课结果........");
		System.out.println("排课成功的课程数 " + scheduledCourses.size());
		int count = 0;
		for(Segment segment: segmentCourseMap.keySet()) {
			count = count + segmentCourseMap.get(segment).size();
		}
		System.out.println("保存在验证Map里的课程 数: " + count);
		System.out.println("排课失败的课程数 " + unScheuledCourses.size());
		for(Segment segment: segmentCourseMap.keySet()) {
			System.out.println();
			System.out.println(segment + "......的课程");
			for(Course course:segmentCourseMap.get(segment)) {
				System.out.println(course);
			}
		}
	}
	
	
	public void showTeacherCourseTable() {
		System.out.println("...........显示教师课表。。。。。。。。。");
		int count = 0;
		for(Teacher teacher: teacherCourseMap.keySet()) {
			count += teacherCourseMap.get(teacher).size();
		}
		System.out.println("教师课表中的课程数量是    。。。。。。" + count);
		for(Teacher teacher: teacherCourseMap.keySet()) {
			List<Course> list = teacherCourseMap.get(teacher);
			System.out.println("\n"+ teacher + "共 " + list.size() + "节课");
			list.sort(new Comparator<Course>() {

				@Override
				public int compare(Course o1, Course o2) {
					return o1.compareTo(o2);
				}
			});
			for(Course course:list) {
				System.out.println(course);
			}
		}
	}
	
	
	public void showFailureCourses() {
		System.out.println("..........显示排课失败的课程......");
		for(Course course: unScheuledCourses) {
			System.out.println(course);
		}
	}
	/**
	 * 匹配课程与时间片, 如果匹配成功，则设置课程的时间
	 * @param segment
	 * @param course
	 * @return 匹配成功返回true, 否则返回false
	 */
	private boolean _doScheduleInternal(ClassSegment segment, Course course) {
		if(segment.support(course)) {
			course.setSegment(segment);
			segment.addConstrant(new ExcludeTeacherConstraint(course.getTeachers()));
			segment.removeAllAdministractiveClasses(course.getAdministractiveClasses());
			_saveToMap(segment, course);
			return true;
		}
		
		return false;
	}
	
	/**
	 * 
	 */
	private void _saveToMap(Segment segment, Course course) {
		_saveToTeacherMap(segment, course);
		_saveToClassMap(segment, course);
		_saveToCourseMap(segment, course);
		_saveToTeacherCourseMap(course);
	}

	
	private void _saveToTeacherCourseMap(Course course) {
		Set<Teacher> teachers2 = course.getTeachers();
		for (Teacher teacher : teachers2) {
			List<Course> list = teacherCourseMap.get(teacher);
			 if(list == null) {
				 list = new ArrayList<>();
				 teacherCourseMap.put(teacher, list);
			 }
			 list.add(course);
			 teacherCourseCount++;
			 
		}
	}
	/**
	 * @param segment
	 * @param course
	 */
	private void _saveToTeacherMap(Segment segment, Course course) {
		Set<Teacher> list = segmentTeacherMap.get(segment);
		if(list == null) {
			list = new HashSet<>();
			segmentTeacherMap.put(segment, list);
		}
		list.addAll(course.getTeachers());
	}

	
	private void _saveToClassMap(Segment segment, Course course) {
		List<AdministrativeClass> list = segmentClassMap.get(segment);
		if(list == null) {
			list = new ArrayList<>();
			segmentClassMap.put(segment, list);
		}
		if(!(course instanceof TeachingClass))
			list.addAll(course.getAdministractiveClasses());
	}

	private void _saveToCourseMap(Segment segment, Course course) {
		List<Course> list = segmentCourseMap.get(segment);
		if(list == null) {
			list = new ArrayList<>();
			segmentCourseMap.put(segment, list);
		}
		list.add(course);
	}


	/**
	 * 验证排课结果
	 * @return
	 */
	public boolean validateSchedule() {
		// TODO 验证排课结果
		int conflictSegmentCount = 0;
		for(Segment segment: segmentCourseMap.keySet()) {
			int courseCountPerSegment = segmentCourseMap.get(segment).size();
			int teacherCountPerSegment = segmentTeacherMap.get(segment).size();
			if(teacherCountPerSegment < courseCountPerSegment) {
				System.out.println("segment: " + segment.toString() + "教师重复排课");
				conflictSegmentCount++;
			}
		}
		
		if(conflictSegmentCount > 0 ) {
			System.err.println("排课验证失败! 有" + conflictSegmentCount + "处错误");
		}
		return conflictSegmentCount ==0;
	}
	
	/**
	 * 第二种验证排课冲突的方式
	 * @return
	 */
	public boolean validateSchedule2() {
		System.out.println("。。.....显示验证结果 ........");
		int confilictCount = 0;
		for(Segment segment: segmentCourseMap.keySet()) {
			Map<Teacher, Integer> map = new HashMap<>();
			Map<Teacher, List<Course>> map2 = new HashMap<>();
			for(Course course: segmentCourseMap.get(segment)) {
				for (Teacher teacher : course.getTeachers()) {
					map.put(teacher,(map.get(teacher)==null?0:map.get(teacher)) + 1);
					List<Course> list = map2.get(teacher);
					if(list == null) {
						list = new ArrayList<>();
						map2.put(teacher, list);
					}
					list.add(course);
				}
			}
			
			for(Teacher teacher: map.keySet()) {
				if(map.get(teacher) > 1) {
					confilictCount ++;
					System.out.println(teacher + "在时间片 " + segment + " 排课重复。。。。。。。。。。重复的课程有" );
					List<Course> list = map2.get(teacher);
					for (Course course : list) {
						System.out.println(course);
					}
				}
			}
			
		}
		
		if(confilictCount > 0) {
			System.out.println("老师重复排课的数量是 :" + confilictCount);
		}
		
		
		return confilictCount == 0;
	}
	
	public int validateSheduledCourses() {
		int confilictCount=0;
		for(int i =0 ; i<scheduledCourses.size(); i++) {
			Course course = scheduledCourses.get(i);
			for(int j= i+1; j<scheduledCourses.size(); j++) {
				Course course2 = scheduledCourses.get(j);
				if(_isConfilct(course, course2))
					confilictCount++;
			}
		}
		
		System.out.println("conflict count is " + confilictCount);
		return confilictCount;
	}
	
	private boolean _isConfilct(Course course1, Course course2) {

			return _checkTeacherConflict(course1, course2) 
					&& _checkClassConflict(course1, course2);
		
	}


	/**
	 * @param course1
	 * @param course2
	 */
	private boolean _checkTeacherConflict(Course course1, Course course2) {

			if(!course1.equals(course2)
					&&course1.getSegment().equals(course2.getSegment())){
				Set<Teacher> teachers = new HashSet<>();
				teachers.addAll(course1.getTeachers());
				teachers.retainAll(course2.getTeachers());
				if(teachers.size()>0)
					return true;
			}
		
		return false;
	}
	
	/**
	 * 
	 * @param course1
	 * @param course2
	 * @return
	 */
	private boolean _checkClassConflict(Course course1, Course course2) {
		if(!course1.equals(course2)
				&&course1.getSegment().equals(course2.getSegment())) {
		
			Set<AdministrativeClass> classes = new HashSet<>();
			classes.addAll(course1.getAdministractiveClasses());
			classes.retainAll(course2.getAdministractiveClasses());
			if(classes.size()>0)
				return true;
		}
		
		return false;
	}
	
	public void showSegments() {
		for(Segment segment: segments) {
			System.out.println(segment);
		}
	}


	public Deque<ClassSegment> getSegments() {
		return segments;
	}


	public Deque<Course> getCourses() {
		return courses;
	}


	
}
